%%%-------------------------------------------------------------------
%%% @author mirahs(2892727493@qq.com)
%%% @doc
%%%     MySQL 管理器
%%% @end
%%%-------------------------------------------------------------------
-module(db_mysql_mgr).

-behaviour(gen_server).

-export([start_link/0]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-include("common.hrl").
-include("env.hrl").

-record(state, {dbs = []}).

-define(timer_reconnect, erlang:send_after(timer:seconds(3), erlang:self(), timer_reconnect)).


%%%===================================================================
%%% Spawning and gen_server implementation
%%%===================================================================

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).


init([]) ->
    Args = env_mgr:get(?ENV_DB_MYSQL),

    Dbs = proplists:get_value(dbs, Args),
    db_init(Dbs, Args),

    Mod = proplists:get_value(mod, Args),
    Mod:ver_upgrade(Dbs),

    erlang:process_flag(trap_exit, true),

    {ok, #state{}}.

handle_call(_Request, _From, State) ->
    {reply, ok, State}.

handle_cast(_Request, State) ->
    {noreply, State}.

handle_info({'EXIT', Pid, _Reason}, State = #state{dbs = Dbs}) ->
    Db = erlang:erase(Pid),
    ?INFO("MySQL [~p] 连接断开，准备重连", [Db]),

    ?timer_reconnect,

    {noreply, State#state{dbs = [Db | Dbs]}};
handle_info(timer_reconnect, State = #state{dbs = Dbs}) ->
    Dbs2 = db_reconnect(Dbs, []),

    ?IF(Dbs2 =/= [], ?timer_reconnect, ok),

    {noreply, State#state{dbs = Dbs2}};
handle_info(_Info, State) ->
    ?ERR("未知 _Info:~p~n", [_Info]),
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.


%%%===================================================================
%%% Internal functions
%%%===================================================================

db_init([], _Args) ->
    ok;
db_init([Db | Dbs], Args) ->
    case proplists:get_value(Db, Args) of
        {Host, Port, User, Password, Database} ->
            Options = [{host, Host}, {port, Port}, {user, User}, {password, Password}, {database, Database}, {keep_alive, true}],
            erlang:put(Db, Options),

            {ok, Pid} = mysql:start_link(Options),
            erlang:put(Pid, Db),
            erlang:register(util_type:list_to_atom(lists:concat(["mysql_", Db])), Pid),

            db_init(Dbs, Args);
        _ ->
            erlang:throw({error, util_io:fbin(<<"db [~] 无配置信息">>, [Db])})
    end.

db_reconnect([], DbsAcc) ->
    DbsAcc;
db_reconnect([Db | Dbs], DbsAcc) ->
    Options = erlang:get(Db),
    case mysql:start_link(Options) of
        {ok, Pid} ->
            ?INFO("MySQL [~p] 重连成功", [Db]),
            erlang:put(Pid, Db),
            erlang:register(util_type:list_to_atom(lists:concat(["mysql_", Db])), Pid),
            db_reconnect(Dbs, DbsAcc);
        _Other ->
            db_reconnect(Dbs, [Db | DbsAcc])
    end.
